Popular Searches
Popular Course Categories
Popular Courses

Working with NumPy Arrays and Numerical Operations

Working with NumPy Arrays and Numerical Operations

Python Data Analysis and Visualization

Working with NumPy Arrays and Numerical Operations

Course: Python Training

Topic: Working with NumPy Arrays and Numerical Operations

Course Link: Python Training Course at JustAcademy

Demo Link: Register for a Course Demo

Website: Visit JustAcademy


1. Introduction to NumPy

NumPy stands for Numerical Python. It is a fundamental Python library used for numerical computing, mathematical operations, scientific calculations, data analysis, and multidimensional array processing. NumPy provides an efficient array object called ndarray, which can store elements in one or more dimensions and perform operations on entire arrays.

Python lists are useful for general-purpose programming, but NumPy arrays provide specialized functionality for numerical data. They support vectorized calculations, mathematical functions, statistical operations, matrix manipulation, broadcasting, and efficient memory usage for many numerical workloads.

NumPy is widely used in data science, machine learning, scientific research, engineering, financial analysis, image processing, and artificial intelligence.

Learning Objectives

  • Understand NumPy and its importance in numerical computing.
  • Install NumPy and import it into a Python program.
  • Create one-dimensional, two-dimensional, and multidimensional arrays.
  • Understand array dimensions, shape, size, and data types.
  • Perform arithmetic and mathematical operations on arrays.
  • Access array elements using indexing and slicing.
  • Filter data using Boolean conditions.
  • Understand broadcasting and vectorized operations.
  • Perform statistical calculations and matrix operations.
  • Reshape, combine, split, sort, and copy arrays.
  • Apply NumPy to a practical numerical data analysis project.

2. Installing NumPy

NumPy must be installed in the Python environment before it can be imported, unless it is already included in that environment.

Install Using pip

python -m pip install numpy

To verify the installation, open a Python terminal or create a Python file and execute the following code.

import numpy as np

print(np.__version__)

The command displays the installed NumPy version. The alias np is the conventional short name used when importing NumPy.

Importing NumPy

import numpy as np

numbers = np.array([10, 20, 30, 40])
print(numbers)

Output:

[10 20 30 40]

The import numpy as np statement makes NumPy functions available through the np prefix. For example, np.array() creates an array and np.mean() calculates an arithmetic mean.

3. Understanding NumPy Arrays

A NumPy array is an object of the ndarray class. It stores elements in a structured multidimensional format and generally uses a common data type for its elements.

3.1 One-Dimensional Array

A one-dimensional array contains elements along a single axis.

import numpy as np

numbers = np.array([10, 20, 30, 40, 50])

print(numbers)

Output:

[10 20 30 40 50]

This array has five elements arranged along one dimension.

3.2 Two-Dimensional Array

A two-dimensional array contains rows and columns. It is useful for representing tables, matrices, and structured numerical data.

import numpy as np

matrix = np.array([
    [1, 2, 3],
    [4, 5, 6]
])

print(matrix)

Output:

[[1 2 3]
 [4 5 6]]

This array contains two rows and three columns.

3.3 Three-Dimensional Array

A three-dimensional array can represent multiple matrices or batches of numerical data.

import numpy as np

data = np.array([
    [[1, 2], [3, 4]],
    [[5, 6], [7, 8]]
])

print(data)
print("Dimensions:", data.ndim)
print("Shape:", data.shape)

Output:

[[[1 2]
  [3 4]]

 [[5 6]
  [7 8]]]
Dimensions: 3
Shape: (2, 2, 2)

The array contains two blocks, with two rows and two columns in each block.

4. Creating NumPy Arrays

NumPy provides several functions for creating arrays from existing data or generating arrays with predefined values and patterns.

4.1 Creating an Array from a List

import numpy as np

numbers = np.array([1, 2, 3, 4, 5])
print(numbers)

4.2 Creating an Array of Zeros

The np.zeros() function creates an array filled with zeros.

import numpy as np

zeros = np.zeros(5)
print(zeros)

Output:

[0. 0. 0. 0. 0.]

The default data type is generally a floating-point type. You can specify another type using the dtype parameter.

zeros = np.zeros(5, dtype=int)
print(zeros)

Output:

[0 0 0 0 0]

4.3 Creating an Array of Ones

import numpy as np

ones = np.ones((2, 3))
print(ones)

Output:

[[1. 1. 1.]
 [1. 1. 1.]]

The tuple (2, 3) specifies two rows and three columns.

4.4 Creating an Array with a Range of Values

The np.arange() function creates values at regular intervals. Its stop value is normally excluded.

import numpy as np

numbers = np.arange(1, 11)
print(numbers)

Output:

[ 1  2  3  4  5  6  7  8  9 10]

You can also specify a step size.

numbers = np.arange(0, 20, 5)
print(numbers)

Output:

[ 0  5 10 15]

4.5 Creating Evenly Spaced Values with linspace()

The np.linspace() function generates a specified number of evenly spaced values between a starting point and an ending point. By default, the ending point is included.

import numpy as np

numbers = np.linspace(0, 10, 6)
print(numbers)

Output:

[ 0.  2.  4.  6.  8. 10.]

Use linspace() when the required number of samples matters more than the step size.

4.6 Creating an Identity Matrix

An identity matrix contains ones along the main diagonal and zeros elsewhere.

import numpy as np

identity = np.eye(3)
print(identity)

Output:

[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

4.7 Creating an Array of Random Values

NumPy's random number generator creates pseudorandom values for simulations, sampling, testing, and numerical experiments.

import numpy as np

rng = np.random.default_rng(42)

numbers = rng.integers(1, 101, size=5)
print(numbers)

The seed makes the generated sequence reproducible with the same NumPy generator behavior. The upper bound of integers() is excluded, so this example generates integers from 1 through 100.

5. Important Attributes of NumPy Arrays

NumPy arrays provide attributes that describe their structure, element count, data type, and memory layout.

Attribute Description Example
ndim Number of dimensions or axes array.ndim
shape Size along each dimension array.shape
size Total number of elements array.size
dtype Data type of array elements array.dtype
itemsize Bytes used by each element array.itemsize
nbytes Total bytes occupied by array element data array.nbytes

Example

import numpy as np

data = np.array([
    [10, 20, 30],
    [40, 50, 60]
], dtype=np.int32)

print("Dimensions:", data.ndim)
print("Shape:", data.shape)
print("Total elements:", data.size)
print("Data type:", data.dtype)
print("Bytes per element:", data.itemsize)
print("Element storage bytes:", data.nbytes)

Output:

Dimensions: 2
Shape: (2, 3)
Total elements: 6
Data type: int32
Bytes per element: 4
Element storage bytes: 24

The nbytes attribute reports the memory occupied by the array's element data. It does not include all Python object overhead or necessarily all memory associated with referenced objects.

6. Understanding NumPy Data Types

NumPy uses data types to determine how values are represented in memory and how arithmetic operations behave. Choosing an appropriate data type can improve memory efficiency and help prevent unexpected numerical results.

Common NumPy Data Types

Data Type Description Example
np.int32 32-bit signed integer np.array([10, 20], dtype=np.int32)
np.int64 64-bit signed integer np.array([100, 200], dtype=np.int64)
np.float32 32-bit floating-point number np.array([1.5, 2.5], dtype=np.float32)
np.float64 64-bit floating-point number np.array([1.25, 3.75], dtype=np.float64)
np.bool_ Boolean value np.array([True, False])
np.str_ String value np.array(["A", "B"])

Specifying a Data Type

import numpy as np

numbers = np.array([1, 2, 3, 4], dtype=np.float64)

print(numbers)
print(numbers.dtype)

Output:

[1. 2. 3. 4.]
float64

Converting an Array's Data Type

The astype() method creates an array with a converted data type.

import numpy as np

numbers = np.array([1.2, 2.8, 3.5])
integers = numbers.astype(int)

print(integers)

Output:

[1 2 3]

Converting floating-point values to integers truncates the fractional portion rather than rounding to the nearest integer. Data-type conversions may also lose precision or change value ranges, so select the target type carefully.

7. Indexing NumPy Arrays

Indexing retrieves individual elements from an array. NumPy uses zero-based indexing, which means the first element has index zero. Negative indices count backward from the end.

7.1 Accessing Elements in a One-Dimensional Array

import numpy as np

numbers = np.array([10, 20, 30, 40, 50])

print(numbers[0])
print(numbers[2])
print(numbers[-1])

Output:

10
30
50

7.2 Accessing Elements in a Two-Dimensional Array

For a two-dimensional array, use array[row_index, column_index].

import numpy as np

matrix = np.array([
    [10, 20, 30],
    [40, 50, 60]
])

print(matrix[0, 1])
print(matrix[1, 2])

Output:

20
60

7.3 Updating Array Elements

import numpy as np

numbers = np.array([10, 20, 30, 40])

numbers[1] = 99

print(numbers)

Output:

[10 99 30 40]

Array elements can be changed by assigning compatible values. The value must be representable by the array's data type.

8. Slicing NumPy Arrays

Slicing selects a range of array elements. The common syntax is array[start:stop:step]. The start index is included, while the stop index is excluded.

8.1 Basic Slicing

import numpy as np

numbers = np.array([10, 20, 30, 40, 50, 60])

print(numbers[1:4])
print(numbers[:3])
print(numbers[3:])
print(numbers[::2])

Output:

[20 30 40]
[10 20 30]
[40 50 60]
[10 30 50]

8.2 Reversing an Array

import numpy as np

numbers = np.array([1, 2, 3, 4, 5])

print(numbers[::-1])

Output:

[5 4 3 2 1]

8.3 Slicing a Two-Dimensional Array

import numpy as np

matrix = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])

print(matrix[0:2, 1:3])

Output:

[[2 3]
 [5 6]]

The first slice selects rows 0 and 1. The second slice selects columns 1 and 2.

Important Note: Views and Copies

Basic slicing often returns a view of the original array rather than an independent copy. Changes made through the view can therefore change the original array.

import numpy as np

original = np.array([10, 20, 30, 40])
view = original[:2]

view[0] = 99

print(original)

Output:

[99 20 30 40]

Use copy() when you need an independent array.

original = np.array([10, 20, 30, 40])
independent = original[:2].copy()

independent[0] = 99

print(original)
print(independent)

Output:

[10 20 30 40]
[99 20]

9. Arithmetic Operations on NumPy Arrays

NumPy supports element-wise arithmetic operations. When arrays have compatible shapes, mathematical operators act on corresponding elements without requiring an explicit Python loop.

9.1 Addition

import numpy as np

a = np.array([10, 20, 30])
b = np.array([1, 2, 3])

print(a + b)

Output:

[11 22 33]

9.2 Subtraction

print(a - b)

Output:

[ 9 18 27]

9.3 Multiplication

print(a * b)

Output:

[10 40 90]

9.4 Division

print(a / b)

Output:

[10. 10. 10.]

Division of integer arrays generally produces floating-point results. Division by zero can produce warnings and values such as infinity or NaN for floating-point arrays. Validate denominators when zero is not meaningful for the calculation.

9.5 Exponentiation

numbers = np.array([2, 3, 4])

print(numbers ** 2)

Output:

[ 4  9 16]

9.6 Modulus

numbers = np.array([10, 15, 20])

print(numbers % 6)

Output:

[4 3 2]

Arithmetic Operators Summary

Operator Operation Example
+ Element-wise addition a + b
- Element-wise subtraction a - b
* Element-wise multiplication a * b
/ Element-wise division a / b
** Element-wise exponentiation a ** 2
% Element-wise remainder a % 2

10. Universal Functions and Mathematical Operations

NumPy provides universal functions, often called ufuncs, that operate element by element on array values. They include mathematical functions for square roots, powers, exponentials, logarithms, trigonometry, and rounding.

10.1 Square Root

import numpy as np

numbers = np.array([4, 9, 16, 25])

print(np.sqrt(numbers))

Output:

[2. 3. 4. 5.]

10.2 Exponential Values

numbers = np.array([0, 1, 2])

print(np.exp(numbers))

This calculates \(e^x\) for every element in the array.

10.3 Logarithms

numbers = np.array([1, np.e, np.e ** 2])

print(np.log(numbers))

The np.log() function calculates natural logarithms. Be aware that zero and negative inputs require special handling in real-valued logarithmic calculations.

10.4 Rounding Values

numbers = np.array([1.234, 2.678, 3.456])

print(np.round(numbers, 2))

Output:

[1.23 2.68 3.46]

10.5 Absolute Values

numbers = np.array([-10, -5, 0, 5, 10])

print(np.abs(numbers))

Output:

[10  5  0  5 10]

10.6 Trigonometric Functions

angles = np.array([0, np.pi / 2, np.pi])

print(np.sin(angles))

NumPy's trigonometric functions use radians rather than degrees.

11. Vectorization in NumPy

Vectorization means applying an operation to an entire array instead of explicitly looping through every element in Python. Many NumPy operations are implemented using optimized compiled code and can be faster and more concise than equivalent Python loops for large numerical datasets.

Example Without Vectorization

numbers = [10, 20, 30, 40]
result = []

for number in numbers:
    result.append(number * 2)

print(result)

Example With Vectorization

import numpy as np

numbers = np.array([10, 20, 30, 40])
result = numbers * 2

print(result)

Output:

[20 40 60 80]

Vectorization reduces repetitive code and often improves performance. The exact speed benefit depends on the operation, data size, memory layout, and the work performed.

12. Broadcasting in NumPy

Broadcasting allows NumPy to perform operations on arrays with different but compatible shapes. It can make calculations possible without manually repeating data to create arrays of identical dimensions.

12.1 Adding a Scalar to an Array

import numpy as np

numbers = np.array([10, 20, 30])

print(numbers + 5)

Output:

[15 25 35]

The scalar value 5 is applied to every array element.

12.2 Broadcasting with Two-Dimensional Data

import numpy as np

matrix = np.array([
    [1, 2, 3],
    [4, 5, 6]
])

row_values = np.array([10, 20, 30])

print(matrix + row_values)

Output:

[[11 22 33]
 [14 25 36]]

The row array has shape (3,), so it can be broadcast across both rows of the matrix.

Broadcasting Rules

  • Compare array dimensions from right to left.
  • Dimensions are compatible when their sizes are equal or one of them is 1.
  • Missing dimensions are treated as if their size were 1.
  • If any pair of dimensions is incompatible, NumPy raises a broadcasting-related error.

Example of Incompatible Shapes

import numpy as np

a = np.array([1, 2, 3])
b = np.array([10, 20])

# These arrays have incompatible shapes.
# result = a + b

The shapes (3,) and (2,) are incompatible for element-wise addition. Reshape or reorganize the data only if that matches the intended calculation.

13. Comparison and Boolean Operations

Comparison operators compare array elements and produce Boolean arrays containing True or False. These results are useful for filtering, validating, and classifying numerical data.

13.1 Comparison Operators

import numpy as np

numbers = np.array([10, 20, 30, 40, 50])

print(numbers > 30)
print(numbers == 20)
print(numbers <= 30)

Output:

[False False False  True  True]
[False  True False False False]
[ True  True  True False False]

13.2 Boolean Indexing

Boolean indexing selects values for which a condition is true.

numbers = np.array([10, 20, 30, 40, 50])

filtered = numbers[numbers > 25]

print(filtered)

Output:

[30 40 50]

13.3 Combining Conditions

Use & for element-wise logical AND and | for element-wise logical OR. Put each comparison inside parentheses.

import numpy as np

numbers = np.array([10, 20, 30, 40, 50])

filtered = numbers[(numbers >= 20) & (numbers <= 40)]
print(filtered)

Output:

[20 30 40]

For element-wise conditions, use NumPy's logical operators rather than Python's scalar and and or operators.

13.4 Using np.where()

The np.where() function selects values based on a condition.

import numpy as np

marks = np.array([35, 75, 48, 90, 25])

result = np.where(marks >= 40, "Pass", "Fail")

print(result)

Output:

['Fail' 'Pass' 'Pass' 'Pass' 'Fail']

The function returns an array containing "Pass" where the condition is true and "Fail" otherwise.

14. Aggregation and Statistical Operations

Aggregation functions summarize numerical data by producing results such as totals, averages, minimums, maximums, and standard deviations. These operations are widely used in data analysis and reporting.

14.1 Sum

import numpy as np

numbers = np.array([10, 20, 30, 40])

print(np.sum(numbers))

Output:

100

14.2 Mean

numbers = np.array([10, 20, 30, 40])

print(np.mean(numbers))

Output:

25.0

14.3 Median

numbers = np.array([10, 20, 30, 40, 100])

print(np.median(numbers))

Output:

30.0

The median is the middle value after the data is ordered. It is often less affected by extreme values than the mean.

14.4 Minimum and Maximum

numbers = np.array([15, 8, 45, 22, 30])

print("Minimum:", np.min(numbers))
print("Maximum:", np.max(numbers))

14.5 Standard Deviation and Variance

numbers = np.array([10, 20, 30, 40, 50])

print("Variance:", np.var(numbers))
print("Standard deviation:", np.std(numbers))

By default, NumPy calculates population variance and population standard deviation using ddof=0. Use ddof=1 when calculating the common sample variance or sample standard deviation estimate.

14.6 Finding Minimum and Maximum Indices

numbers = np.array([12, 45, 7, 89, 34])

print("Minimum index:", np.argmin(numbers))
print("Maximum index:", np.argmax(numbers))

Output:

Minimum index: 2
Maximum index: 3

Common Statistical Functions

Function Purpose
np.sum() Calculates the sum of values
np.mean() Calculates the arithmetic mean
np.median() Calculates the median
np.min() Finds the minimum value
np.max() Finds the maximum value
np.std() Calculates standard deviation
np.var() Calculates variance
np.percentile() Calculates a requested percentile
np.argmax() Returns the index of a maximum value
np.argmin() Returns the index of a minimum value

15. Axis-Based Operations

For multidimensional arrays, the axis parameter specifies the dimension along which an operation is performed. Understanding axes is essential when calculating row-wise or column-wise summaries.

import numpy as np

data = np.array([
    [10, 20, 30],
    [40, 50, 60]
])

print("Total:", np.sum(data))
print("Column totals:", np.sum(data, axis=0))
print("Row totals:", np.sum(data, axis=1))

Output:

Total: 210
Column totals: [50 70 90]
Row totals: [ 60 150]

For a two-dimensional array:

  • axis=0 combines values down the rows, producing a result for each column.
  • axis=1 combines values across the columns, producing a result for each row.

The same principle applies to functions such as np.mean(), np.min(), and np.max().

16. Reshaping NumPy Arrays

Reshaping changes an array's dimensions without changing its sequence of elements. The new shape must be compatible with the total number of elements.

16.1 Using reshape()

import numpy as np

numbers = np.arange(1, 7)
matrix = numbers.reshape(2, 3)

print(matrix)

Output:

[[1 2 3]
 [4 5 6]]

The original array contains six elements, and the shape (2, 3) also requires six elements.

16.2 Using -1 in reshape()

NumPy can infer one dimension when the other dimensions are provided.

numbers = np.arange(12)
matrix = numbers.reshape(3, -1)

print(matrix.shape)

Output:

(3, 4)

Only one dimension can be inferred using -1 in a reshape operation.

16.3 Flattening an Array

import numpy as np

matrix = np.array([
    [1, 2, 3],
    [4, 5, 6]
])

flat = matrix.flatten()
print(flat)

Output:

[1 2 3 4 5 6]

flatten() returns a copy. The ravel() method returns a flattened array and may return a view when possible.

17. Transposing and Swapping Axes

Transposing rearranges the axes of an array. For a two-dimensional matrix, it exchanges rows and columns.

import numpy as np

matrix = np.array([
    [1, 2, 3],
    [4, 5, 6]
])

print(matrix.T)

Output:

[[1 4]
 [2 5]
 [3 6]]

The original shape is (2, 3), while the transposed shape is (3, 2). Transposition is commonly used in matrix calculations and data transformations.

18. Combining and Splitting Arrays

NumPy provides functions to join arrays horizontally or vertically and split arrays into smaller parts. The arrays must satisfy the dimensional requirements of the operation.

18.1 Concatenating Arrays

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

result = np.concatenate((a, b))
print(result)

Output:

[1 2 3 4 5 6]

18.2 Stacking Arrays Vertically

import numpy as np

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

result = np.vstack((a, b))
print(result)

Output:

[[1 2]
 [3 4]
 [5 6]
 [7 8]]

18.3 Stacking Arrays Horizontally

result = np.hstack((a, b))
print(result)

Output:

[[1 2 5 6]
 [3 4 7 8]]

18.4 Splitting an Array

import numpy as np

numbers = np.array([10, 20, 30, 40, 50, 60])

parts = np.split(numbers, 3)

for part in parts:
    print(part)

Output:

[10 20]
[30 40]
[50 60]

np.split() requires equal division when an integer number of sections is specified. Use np.array_split() when the array cannot be divided evenly into the requested number of sections.

19. Sorting and Searching Arrays

Sorting organizes numerical values in ascending or descending order. Searching functions help locate matching values or determine where a value belongs in a sorted array.

19.1 Sorting Values

import numpy as np

numbers = np.array([40, 10, 50, 20, 30])

print(np.sort(numbers))

Output:

[10 20 30 40 50]

19.2 Sorting in Descending Order

numbers = np.array([40, 10, 50, 20, 30])

descending = np.sort(numbers)[::-1]
print(descending)

Output:

[50 40 30 20 10]

19.3 Finding Matching Positions

import numpy as np

numbers = np.array([10, 20, 30, 20, 40])

positions = np.where(numbers == 20)
print(positions[0])

Output:

[1 3]

19.4 Finding Unique Values

numbers = np.array([10, 20, 10, 30, 20, 40])

print(np.unique(numbers))

Output:

[10 20 30 40]

20. Matrix Operations and Linear Algebra

NumPy supports matrix multiplication, dot products, transposition, determinants, inverses, and other linear algebra operations through its standard functions and the numpy.linalg module.

20.1 Element-Wise Multiplication vs. Matrix Multiplication

Element-wise multiplication uses the * operator. Matrix multiplication uses the @ operator or np.matmul().

import numpy as np

a = np.array([
    [1, 2],
    [3, 4]
])

b = np.array([
    [5, 6],
    [7, 8]
])

print("Element-wise multiplication:")
print(a * b)

print("Matrix multiplication:")
print(a @ b)

Output:

Element-wise multiplication:
[[ 5 12]
 [21 32]]

Matrix multiplication:
[[19 22]
 [43 50]]

20.2 Dot Product

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(np.dot(a, b))

Output:

32

The dot product is calculated as 1*4 + 2*5 + 3*6 = 32.

20.3 Determinant of a Matrix

import numpy as np

matrix = np.array([
    [1, 2],
    [3, 4]
])

print(np.linalg.det(matrix))

The determinant of this matrix is approximately -2, subject to floating-point representation.

20.4 Solving Linear Equations

The np.linalg.solve() function solves a system of linear equations represented by a coefficient matrix and a right-hand-side vector.

import numpy as np

A = np.array([
    [2, 1],
    [1, 3]
])

b = np.array([5, 6])

solution = np.linalg.solve(A, b)
print(solution)

Output:

[1.8 1.4]

The solution means that \(x=1.8\) and \(y=1.4\) satisfy the equations \(2x+y=5\) and \(x+3y=6\).

21. Handling Missing and Invalid Numerical Values

Numerical datasets may contain missing or invalid values. NumPy commonly represents missing numerical values using np.nan for floating-point arrays. It also provides functions for identifying NaN values and calculating statistics while ignoring them.

21.1 Identifying NaN Values

import numpy as np

data = np.array([10.0, 20.0, np.nan, 40.0])

print(np.isnan(data))

Output:

[False False  True False]

21.2 Calculating a Mean While Ignoring NaN

data = np.array([10.0, 20.0, np.nan, 40.0])

print(np.mean(data))
print(np.nanmean(data))

Output:

nan
23.333333333333332

np.mean() propagates NaN in this example, while np.nanmean() ignores NaN values. Always decide whether missing values should be ignored, replaced, or treated as errors according to the meaning of the data.

21.3 Replacing NaN Values

import numpy as np

data = np.array([10.0, 20.0, np.nan, 40.0])

cleaned = np.nan_to_num(data, nan=0.0)
print(cleaned)

Output:

[10. 20.  0. 40.]

Replacing missing values with zero is appropriate only when zero has a valid meaning for the problem. In other situations, use a suitable imputation strategy or preserve the missing values.

22. Random Sampling and Reproducibility

NumPy's random generator can produce random numbers, select samples, and shuffle data. These features are useful in simulations, statistical experiments, testing, and machine learning.

22.1 Generating Random Floating-Point Values

import numpy as np

rng = np.random.default_rng(42)

values = rng.random(5)
print(values)

The values are generated between 0 inclusive and 1 exclusive.

22.2 Random Integers

rng = np.random.default_rng(42)

values = rng.integers(1, 11, size=5)
print(values)

The integers range from 1 through 10 because the upper bound is excluded.

22.3 Shuffling an Array

import numpy as np

rng = np.random.default_rng(42)
numbers = np.array([1, 2, 3, 4, 5])

rng.shuffle(numbers)
print(numbers)

shuffle() changes the order of the array in place. Use a seeded generator when reproducibility is important, and do not use NumPy's pseudorandom generator for cryptographic secrets.

23. Saving and Loading NumPy Arrays

NumPy can save arrays to disk and load them later. This is useful for intermediate data, scientific experiments, and applications that repeatedly work with the same numerical dataset.

23.1 Saving an Array as a Binary File

import numpy as np

numbers = np.array([10, 20, 30, 40])

np.save("numbers.npy", numbers)

23.2 Loading a Saved Array

import numpy as np

loaded = np.load("numbers.npy")
print(loaded)

23.3 Saving Multiple Arrays

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

np.savez("arrays.npz", first=a, second=b)

with np.load("arrays.npz") as archive:
    print(archive["first"])
    print(archive["second"])

Load files only from trusted sources when using NumPy formats that may involve pickled Python objects. Avoid enabling pickle loading for untrusted data.

23.4 Saving Data as a Text File

import numpy as np

data = np.array([
    [10, 20, 30],
    [40, 50, 60]
])

np.savetxt("data.csv", data, delimiter=",", fmt="%d")

loaded = np.loadtxt("data.csv", delimiter=",")
print(loaded)

Text formats are convenient for interoperability, although binary formats may preserve numerical data more efficiently and accurately.

24. Improving Numerical Performance

NumPy is designed for efficient numerical operations, but performance depends on data size, memory usage, array shape, and the selected operations.

  • Use vectorized operations: Prefer array expressions over unnecessary Python loops for numerical calculations.
  • Choose suitable data types: Use an appropriate integer or floating-point type for the required precision and value range.
  • Avoid unnecessary copies: Copying large arrays consumes additional memory.
  • Understand views: Slicing may create a view that shares memory with the original array.
  • Use broadcasting: Avoid manually repeating data when compatible shapes allow broadcasting.
  • Process large datasets carefully: Avoid creating several large temporary arrays unnecessarily.
  • Profile before optimizing: Measure the actual bottleneck rather than assuming a particular operation is slow.
  • Validate numerical results: Floating-point calculations may contain rounding errors.

Example: Efficient Array Calculation

import numpy as np

prices = np.array([100.0, 200.0, 300.0, 400.0])
discount_rate = 0.10

final_prices = prices * (1 - discount_rate)

print(final_prices)

Output:

[ 90. 180. 270. 360.]

Every price is reduced by 10% through a single vectorized expression.

25. Common NumPy Errors and Troubleshooting

Error or Problem Possible Cause Solution
ModuleNotFoundError: No module named 'numpy' NumPy is not installed in the active Python environment Run python -m pip install numpy in the correct environment
IndexError An index is outside the valid range Check array size and valid index values
Broadcasting error Array shapes are incompatible Inspect shape and reshape only when appropriate
Unexpected modification of original data A slice is a view sharing memory Use copy() when independent data is required
Unexpected integer result Integer data type or integer conversion affects the result Inspect dtype and use an appropriate type
Unexpected NaN or infinity Invalid numerical input or division by zero Validate inputs and use appropriate NaN or infinity handling
Reshape error The requested shape has an incompatible element count Ensure the product of dimensions equals the number of elements
Unexpected matrix multiplication result Used element-wise multiplication instead of matrix multiplication Use @ or np.matmul() where appropriate

26. Practical Project: Student Marks Analysis Using NumPy

Project Objective: Create a numerical analysis program that stores student marks in a NumPy array, calculates totals and averages, identifies the highest and lowest marks, and classifies students according to a passing threshold.

Project Requirements

  • Store marks in a NumPy array.
  • Calculate the total and average marks.
  • Find the highest and lowest marks.
  • Identify the positions of the highest and lowest marks.
  • Filter marks that meet the passing threshold.
  • Count the number of students who pass and fail.
  • Display a summary of the results.

Complete Project Code

import numpy as np

student_names = np.array([
    "Aarav",
    "Diya",
    "Kabir",
    "Meera",
    "Rohan"
])

marks = np.array([78, 92, 35, 64, 48])

passing_mark = 40

total_marks = np.sum(marks)
average_marks = np.mean(marks)
highest_mark = np.max(marks)
lowest_mark = np.min(marks)

highest_index = np.argmax(marks)
lowest_index = np.argmin(marks)

passed = marks >= passing_mark
failed = marks < passing_mark

print("Student Marks Analysis")
print("----------------------")
print("Names:", student_names)
print("Marks:", marks)
print("Total marks:", total_marks)
print("Average marks:", round(average_marks, 2))
print("Highest mark:", highest_mark)
print("Highest scorer:", student_names[highest_index])
print("Lowest mark:", lowest_mark)
print("Lowest scorer:", student_names[lowest_index])
print("Students passed:", np.sum(passed))
print("Students failed:", np.sum(failed))
print("Passing marks:", marks[passed])
print("Failing marks:", marks[failed])

Expected Output

Student Marks Analysis
----------------------
Names: ['Aarav' 'Diya' 'Kabir' 'Meera' 'Rohan']
Marks: [78 92 35 64 48]
Total marks: 317
Average marks: 63.4
Highest mark: 92
Highest scorer: Diya
Lowest mark: 35
Lowest scorer: Kabir
Students passed: 4
Students failed: 1
Passing marks: [78 92 64 48]
Failing marks: [35]

Project Explanation

  1. Create arrays: Store student names and marks in NumPy arrays.
  2. Calculate totals: Use np.sum() to find the sum of marks.
  3. Calculate averages: Use np.mean() to calculate the average.
  4. Find extremes: Use np.max() and np.min().
  5. Identify students: Use np.argmax() and np.argmin() to locate the positions of the highest and lowest marks.
  6. Apply conditions: Create Boolean arrays to classify marks.
  7. Count results: Sum Boolean values to count students who pass or fail.

Project Extensions

  • Add subject-wise marks using a two-dimensional array.
  • Calculate averages for each student and each subject.
  • Identify students whose marks are above the class average.
  • Calculate standard deviation to examine mark variation.
  • Save the analysis to a CSV file.
  • Handle missing marks and validate that marks fall within the allowed range.

27. Practice Exercises

  1. Create a NumPy array containing the integers from 1 to 20.
  2. Create a 3-by-3 array containing the numbers 1 through 9.
  3. Generate an array of ten zeros and an array of ten ones.
  4. Generate 11 evenly spaced values between 0 and 100.
  5. Calculate the sum, mean, median, minimum, and maximum of an array.
  6. Multiply every element in an array by 5.
  7. Extract all even numbers from an array using Boolean indexing.
  8. Reverse a one-dimensional array using slicing.
  9. Reshape a 12-element array into a 3-by-4 matrix.
  10. Calculate row-wise and column-wise totals for a two-dimensional array.
  11. Combine two arrays using np.concatenate().
  12. Sort an array in ascending and descending order.
  13. Find the indices of the smallest and largest values.
  14. Replace NaN values using an appropriate missing-data strategy.
  15. Build a marks-analysis program with pass/fail classification.

28. Interview Questions and Answers

Q1. What is NumPy?

Answer: NumPy is a Python library for numerical computing. It provides multidimensional arrays, mathematical functions, statistical operations, broadcasting, and linear algebra functionality.

Q2. What is an ndarray?

Answer: An ndarray is NumPy's main array object. It stores elements in a multidimensional structure and generally uses a common data type for its elements.

Q3. What is the difference between a Python list and a NumPy array?

Answer: Python lists can contain mixed object types and are useful for general-purpose collections. NumPy arrays are designed for numerical and multidimensional operations, support vectorized calculations, and often use memory more efficiently for homogeneous numerical data.

Q4. What is the difference between shape, size, and ndim?

Answer: shape gives the size of each dimension, size gives the total number of elements, and ndim gives the number of dimensions.

Q5. What is the difference between arange() and linspace()?

Answer: arange() generates values based on a step size and normally excludes the stop value. linspace() generates a specified number of evenly spaced values and includes the endpoint by default.

Q6. What is vectorization?

Answer: Vectorization applies operations to whole arrays instead of explicitly iterating through each element in Python. It often produces simpler and faster numerical code.

Q7. What is broadcasting?

Answer: Broadcasting allows NumPy to perform operations on arrays with compatible shapes without requiring them to have identical dimensions.

Q8. What is Boolean indexing?

Answer: Boolean indexing selects elements based on a Boolean condition. For example, numbers[numbers > 10] selects values greater than 10.

Q9. What is the difference between * and @ for arrays?

Answer: The * operator performs element-wise multiplication. The @ operator performs matrix multiplication when the operand shapes satisfy the required rules.

Q10. What is the difference between a view and a copy?

Answer: A view shares underlying data with another array, so changes may affect both. A copy has independent array data. Use copy() when modifications must not affect the original.

Q11. How do you handle missing numerical values?

Answer: Use np.isnan() to identify NaN values and functions such as np.nanmean() to calculate certain statistics while ignoring NaN. Replacing missing values should be based on the data's meaning.

Q12. What is the purpose of axis in NumPy?

Answer: The axis parameter specifies the dimension along which an operation is performed. For a two-dimensional array, axis=0 generally produces column-wise results and axis=1 produces row-wise results.

Q13. How do you reshape a NumPy array?

Answer: Use reshape() to change the dimensions while preserving the number of elements. The requested shape must be compatible with the array's total size.

Q14. How do you find the largest value in an array?

Answer: Use np.max(array) to return the largest value and np.argmax(array) to return the index of a maximum value along the selected axis.

Q15. How can you save a NumPy array?

Answer: Use np.save() to save an array in NPY format, np.savez() to save multiple arrays in an NPZ archive, and np.savetxt() to export numerical data to a text file.

29. Quick Revision Checklist

  • NumPy is a fundamental library for numerical computing in Python.
  • np.array() creates arrays from Python sequences.
  • np.zeros(), np.ones(), np.arange(), and np.linspace() create arrays in different ways.
  • ndim, shape, size, and dtype describe an array.
  • Indexing and slicing retrieve selected array elements.
  • Basic slicing often returns a view; use copy() for independent data.
  • Arithmetic operators perform element-wise operations.
  • Vectorization simplifies numerical calculations.
  • Broadcasting allows operations on compatible array shapes.
  • Boolean indexing filters values based on conditions.
  • Aggregation functions calculate totals, averages, and statistical summaries.
  • The axis parameter controls the dimension used by many operations.
  • reshape(), transpose(), and concatenation functions manipulate array structures.
  • The @ operator performs matrix multiplication.
  • NumPy supports random sampling and saving or loading numerical arrays.
  • Correct data types, validation, and memory management help avoid numerical errors.

30. Conclusion

NumPy arrays and numerical operations are essential tools for Python programming, data analysis, scientific computing, and machine learning. By understanding array creation, indexing, slicing, arithmetic operations, broadcasting, statistics, reshaping, and matrix calculations, developers can process numerical datasets efficiently and clearly.

Practical experience is the best way to build confidence with NumPy. Start with small arrays, verify their shapes and data types, experiment with vectorized operations, and gradually move toward multidimensional datasets and real-world analytical projects.

31. Further Learning Resources

whatsapp